package agent;

import common.Move;
import common.Side;
import java.util.LinkedList;
import java.util.List;

/**
 * All methods in this class must take into consideration that, in the Domino
 * game, a player may play two or more times in a row. This happens if the other
 * player still has pieces but is not able to play any of them and is forced to
 * pass its turn.
 */
public class AlphaBeta extends GameAlgorithm<AgentSearchState> {

    /**
     * Receives the successors of the current fictitious state (where the
     * opponent pieces are "guessed") and returns an array with the minimax
     * values. It is called when the agent doesn't know exactly what pieces its
     * opponent has.
     *
     * @param successors
     * @return
     */
    //XXX para que atributos para infinity?!?
    public double[] minimaxValues(LinkedList<AgentSearchState> successors) {
        double[] minimaxValues = new double[successors.size()];
//        System.out.println(successors.size());
        for (int i = 0; i < successors.size(); i++) {
            minimaxValues[i] = maxValue(successors.get(i), Double.NEGATIVE_INFINITY, Double.POSITIVE_INFINITY, 1);
        }
        return minimaxValues;
    }

    /**
     * This is the method that should be called when the agent knows exactly
     * what are the pieces of the opponent (usually, this is only possible in
     * the final phase of the game. Please notice that, when the agent knows
     * exactly what are the pieces of the opponent, the game is no more an
     * imperfect information game and, so, there is no point in simulating
     * fictitious hands.
     *
     * @param currentState
     * @return
     */
    @Override
    public Move takeDecision(AgentSearchState currentState) {
        List<AgentSearchState> sucessors = currentState.getSucessors();
        double moveValue, max = Double.NEGATIVE_INFINITY;
        AgentSearchState bestState = null;
        for (AgentSearchState s : sucessors) {
            //Verificar o player? nao é preciso o getSuccessors já verifica
            moveValue = minValue(s, max, Double.POSITIVE_INFINITY, 1);
            if (max <= moveValue) {
                max = moveValue;
                bestState = s;
            }
        }
        return bestState.getMove();
    }

    private double maxValue(AgentSearchState state, double alpha, double beta, int depth) {
//         porque nao ver consuante o player o min e o max??!?!??! não se faz porque quando acontece um skip à jogada acontece um clone 
//        ao estado em que se troca o player. O maxValue só é chamado quando é o agent a jogar, o minValue só é chamado quando é o 
//        opponent a jogar

        if (depth == depthLimit || state.isEndOfGameState()) {
            return state.evaluate();
        }
        List<AgentSearchState> sucessors = state.getSucessors();
            for (AgentSearchState ass : sucessors) {
                alpha = Math.max(alpha, minValue(ass, alpha, beta, depth + 1));
                if (alpha >= beta) {
                    return beta;
                }
            }
        return alpha;
    }

    private double minValue(AgentSearchState state, double alpha, double beta, int depth) {
        if (depth == depthLimit || state.isEndOfGameState()) {
            return state.evaluate();
        }
        List<AgentSearchState> sucessors = state.getSucessors();
        
            for (AgentSearchState ass : sucessors) {
                beta = Math.min(beta, maxValue(ass, alpha, beta, depth + 1));
                if (beta <= alpha) {
                    return alpha;
                }
            }
        return beta;
    }
}
